{ 
  BPlist v.0.1 
  Conversion from Apple binary plist format to XML plist.
  Warning: some types of data not supported (at this moment):
    <date>, <data>, <uid>
  This program is licensed under the BSD.
  Copyright (c)  2011, Alexey Dolgopolov
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification,
  are permitted provided that the following conditions are met:
  Redistributions of source code must retain the above copyright notice, this list of conditions
  and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, 
  this list of conditions and the following disclaimer in the documentation and/or other materials provided 
  with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  OF SUCH DAMAGE. 
}

unit BPlist;
{$mode objfpc}{$H+}
interface

uses SysUtils;

//convert binary plist to XML plist
function BinaryToXML(inputp,outputp:string):boolean;

implementation

function GetDict(Pos: int64; RefSize: integer): boolean; forward;

var
 Out: text;
 Binary: array of byte;
 OffsetTable: array of int64;
 DictSpace: string;

const
 DEBUG = FALSE;
 DICT = 0;
 ARR = 1;
 STR = 2;
 BOOL = 3;
 INT = 4;
 RL = 5;
 DATE = 6;
 DATA = 7;
 UTF = 8;
 FILL = 9;
 EMP = 10;
 UID = 11;

//n must be >= 0
function pow(x:integer;n:integer):int64; overload;
var 
  buff: int64;
  i: integer;
begin
  if n > 0 then
  begin
    buff := 1;
    for i := 1 to n do
    buff := buff*x;
    pow := buff;
  end
  else
  pow := 0;
  if n = 0 then 
    pow := 1;
end;

//y can be < 0
function pow(x,y:double):double; overload;
begin
  if x = 0 then pow := 0
  else 
  if x > 0 then pow := exp(ln(x)*y)
  else 
  pow := -exp(ln(abs(x))*y);
end;

//convert string like '0101' (64 bit max) to dec
function BinaryToInt(input: string):int64;
var
 i,power: integer;
 bf: int64;
 StrOk: boolean;
begin
  Result := 0;
  power := 0;
  StrOk := true;
  for i := 1 to length(input) do
  if (input[i] <> '0') and (input[i] <> '1') then 
    StrOk := false;

  if StrOk then
  begin
    for i := length(input) downto 1 do 
    begin
      if input[i] <> '0' then
      bf := StrToInt(input[i])*pow(2,power)
      else
      bf := 0;
      Result := Result + bf; 
      power := power + 1;
    end;
  end;
end;

//convert fractional part of bin value to fractional part of dec value
//example: input ='00111' (mean 0.00111), output = 21875 ( mean 0.21875)
function BinFrac2Dec(input: string):string;
var
 temp: double;
 i: longint;
 StrOk: boolean;
begin
 temp := 0;
 Result := '';
 StrOk := true;

 for i := 1 to length(input) do
  if (input[i] <> '0') and (input[i] <> '1') then 
    StrOk := false;

 if StrOk then
 begin
  for i := 1 to length(input) do
  begin
    if input[i] = '1' then
      temp := temp + StrToInt(input[i])*pow(2.0,(-1*i));
  end;

  Result := FloatToStr(temp);
  Delete(Result,1,2);
 end;
end;

//convert string as binary double (64 bit) to string as dec float
function BinaryToReal(input: string):string;
var
 m, exp, intg, frac: string;
 i: longint;
 level: integer;
 StrOk: boolean;
begin
  frac := '';
  intg := '';
  Result := '0';
  StrOk := true;
  level := 0;

  for i := 1 to length(input) do
  if (input[i] <> '0') and (input[i] <> '1') then 
    StrOk := false;
   
  //writeln('input length:',length(input));
  
  if StrOk then
  begin
    if Length(input) = 64 then
    begin
       if input[1] = '1' then
         Result := '-'
       else
         Result := '';
       exp := copy(input,2,11);
       m := copy(input,13,52);
       level := BinaryToInt(exp) - 1023;
       m:= '1'+m;
       intg := copy(m,1,level+1);
       frac := m;
       delete(frac,1,level+1);
       Result := IntToStr(BinaryToInt(intg)) + '.' + BinFrac2Dec(frac);
    end;
  end;
end;

//convert one byte dec to string like '00011111'
function ShowAsBinary(input: byte):string;
var
 quotient:byte;
 remainder:byte;
 res: string[8];
 i: byte;
begin
  quotient := input;
  i := 8;
  res := '';
  Result := '';

  while quotient <> 0 do
  begin
    remainder := quotient mod 2;
    quotient := quotient div 2;
    res := res + IntToStr(remainder);
  end;
  
  for i := 1 to (8-length(res)) do
    Result := Result + '0';
  for i := length(res) downto 1 do
    Result := Result + res[i];

end;

function TypeOfObject(Pos: int64):integer;
var
 tmp: string;
 marker: longint;
 gottype: boolean;
begin
  tmp := ShowAsBinary(Binary[OffsetTable[Pos]]);
  marker := StrToInt(tmp);
  gottype := true;

  case marker of
    1000: TypeOfObject := BOOL;
    1001: TypeOfObject := BOOL;
    1111: TypeOfObject := FILL;
    110011: TypeOfObject := DATE;
    0: TypeOfObject := EMP;
    else
      gottype := false;
  end;
  
  if not gottype then
  begin 
    delete(tmp,5,4);
    marker := StrToInt(tmp);
    case marker of
    1:   TypeOfObject := INT;
    10:  TypeOfObject := RL;
    100: TypeOfObject := DATA;
    101: TypeOfObject := STR;
    110: TypeOfObject := UTF;
    1000:TypeOfObject := UID;
    1010:TypeOfObject := ARR;
    1101:TypeOfObject := DICT;
    end;
  end;
end;

function GetUid(Pos: int64; AsKey: boolean):boolean;
var
 value: string;
begin
value := '';
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// not finished
if AsKey then
  writeln(out,DictSpace+'    <key>',value,'</key>')
else
  writeln(out,DictSpace+'    <uid>',value,'</uid>');
GetUid := true;
end;

function GetStr(Pos: int64; AsKey: boolean):boolean;
var
 value: AnsiString;
 temp: string;
 count, startpos, i, j: longint;
begin
  value := '';
  count := 0;
  j := 0;
  i := 0;
  temp := ShowAsBinary(Binary[OffsetTable[Pos]]);
  delete(temp,1,4);
  GetStr := false;

  //getting number of bytes in string and start position
  if temp <> '1111' then
  begin
     count := BinaryToInt(temp);
     startpos := OffsetTable[Pos]+1;
  end
  else
  begin
    temp := ShowAsBinary(Binary[OffsetTable[Pos]+1]);
    delete(temp,1,4);
    i := pow(2,BinaryToInt(temp));

    temp := '';
      j := 1;
      while j <= i do 
      begin
        temp := temp + ShowAsBinary(Binary[OffsetTable[Pos]+j+1]);
        j := j+1;
      end;
    startpos := OffsetTable[pos]+j+1;
    count := BinaryToInt(temp);  
  end;

  if count >= 0 then 
  begin
      GetStr:= true;
    for i := 0 to count-1 do
    begin
      value := value + Chr(Binary[startpos+i]);
    end;

    if AsKey then
      writeln(out,DictSpace+'    <key>',value,'</key>')
    else
      writeln(out,DictSpace+'    <string>',value,'</string>');
  end;
end;

function GetUnicode(Pos: int64; AsKey: boolean):boolean;
var
 count, shift, startpos, i, j: longint;
 value: widestring;
 temp: string;
begin
  value := '';
  count := 0;
  temp := ShowAsBinary(Binary[OffsetTable[Pos]]);
  delete(temp,1,4);
  GetUnicode := false;

  //getting number of elements in unicode string and start position
  if temp <> '1111' then
  begin
     count := BinaryToInt(temp);
     startpos := OffsetTable[Pos]+1;
  end
  else
  begin
    temp := ShowAsBinary(Binary[OffsetTable[Pos]+1]);
    delete(temp,1,4);
    i := pow(2,BinaryToInt(temp));
    temp := '';
    j := 1;
      while j <= i do 
      begin
        temp := temp + ShowAsBinary(Binary[OffsetTable[Pos]+j+1]);
        j := j+1;
      end;
    startpos := OffsetTable[pos]+j+1;
    count := BinaryToInt(temp);  
  end;

  if count >= 0 then 
  begin
      GetUnicode:= true;
    shift := 0;
    for i := 0 to count-1 do
    begin
      temp := ShowAsBinary(Binary[startpos+shift]) + 
        ShowAsBinary(Binary[startpos+shift+1]);

      value := value + WideChar(BinaryToInt(temp));
      shift := shift + 2;
    end;
    if AsKey then
      writeln(out,DictSpace+'    <key>',value,'</key>')
    else
      writeln(out,DictSpace+'    <string>',value,'</string>');
  end;
end;

function GetDate(Pos: int64; AsKey: boolean):boolean;
var
 value: string;
begin
  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  //not finished
  value := '2010-06-22T19:00:00.000000Z';
  if AsKey then
    writeln(out,DictSpace+'    <key>',value,'</key>')
  else
    writeln(out,DictSpace+'    <date>',value,'</date>');
  GetDate := true;
end;

function GetData(Pos: int64; AsKey: boolean):boolean;
var
 value: AnsiString;
 temp: string;
 count, startpos, i, j: longint;
begin
value := '';
  count := 0;
  temp := ShowAsBinary(Binary[OffsetTable[Pos]]);
  delete(temp,1,4);
  GetData := false;

  //getting number of bytes and start position
  if temp <> '1111' then
  begin
     count := BinaryToInt(temp);
     startpos := OffsetTable[Pos]+1;
  end
  else
  begin
    temp := ShowAsBinary(Binary[OffsetTable[Pos]+1]);
    delete(temp,1,4);
    i := pow(2,BinaryToInt(temp));
    temp := '';
    for j := 1 to i do
      temp := temp + ShowAsBinary(Binary[OffsetTable[Pos]+j+1]);
    startpos := OffsetTable[pos]+j+2;
    count := BinaryToInt(temp);  
  end;

  if count > 0 then 
  begin
      GetData :=  true;
    for i := 0 to count-1 do
    begin
      //how to interpret this data?
      value := value + Chr(Binary[startpos+i]);
    end;
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //not finished
    value := 'PEKBpYGlmYFCPA==';
    if AsKey then
      writeln(out,DictSpace+'    <key>',value,'</key>')
    else
      writeln(out,DictSpace+'    <data>',value,'</data>');
  end;
end;

function GetBool(Pos: int64):boolean;
var
 tmp: string;
begin
  GetBool := false;
  tmp := ShowAsBinary(Binary[OffsetTable[Pos]]);
  if tmp = '00001001' then
  begin
    writeln(out,DictSpace+'    <true/>');
    GetBool := true;
  end;
  if tmp = '00001000' then
  begin
    writeln(out,DictSpace+'    <false/>');
    GetBool := true;
  end;
end;

function GetInt(Pos: int64; AsKey: boolean):boolean;
var
 value, j, i:longint;
 temp: string;
begin
  GetInt := false;
  value := 0;
  temp := ShowAsBinary(Binary[OffsetTable[Pos]]);
  delete(temp,1,4);
  i := pow(2,BinaryToInt(temp));
  if i > 0 then
    GetInt := true;
  temp := '';
  if GetInt then
  begin
    for j := 1 to i do
      temp := temp + ShowAsBinary(Binary[OffsetTable[Pos]+j]);
    value := BinaryToInt(temp);  

    if AsKey then
      writeln(out,DictSpace+'    <key>',value,'</key>')
    else
      writeln(out,DictSpace+'    <integer>',value,'</integer>');
  end;
end;

function GetReal(Pos: int64; AsKey: boolean):boolean;
var
 value,temp: string;
 j, i: longint;
begin
  GetReal := false;
  value := '';
  temp := ShowAsBinary(Binary[OffsetTable[Pos]]);
  delete(temp,1,4);
  i := pow(2,BinaryToInt(temp));
  if i > 0 then
    GetReal := true;
  temp := '';
  if GetReal then
  begin
    for j := 1 to i do
      temp := temp + ShowAsBinary(Binary[OffsetTable[Pos]+j]);
    value := BinaryToReal(temp);  

    if AsKey then
      writeln(out,DictSpace+'    <key>',value,'</key>')
    else
      writeln(out,DictSpace+'    <real>',value,'</real>');
  end;
end;

function GetArray(Pos: int64; RefSize: integer): boolean;
var
 temp, buff: string;
 ReffsCount, i, j, startpos, shift:longint;
 ObjReffs: array of int64;
begin
  DictSpace := DictSpace + '    ';
  writeln(out,DictSpace+'    <array>');
  GetArray := true;
  ReffsCount := 0;
  i := 0; j := 0;
  startpos := 0;
  temp := '';
  buff := '';
  temp := ShowAsBinary(Binary[OffsetTable[Pos]]);
  delete(temp,1,4);

  //getting number of refs in dict
  if temp <> '1111' then
   begin
     ReffsCount := BinaryToInt(temp);
     startpos := OffsetTable[Pos] + 1;
   end
  else
  begin
    temp := ShowAsBinary(Binary[OffsetTable[Pos]+1]);
    delete(temp,1,4);
    i := pow(2,BinaryToInt(temp));
    buff := '';
    for j := 1 to i do
    buff := buff + ShowAsBinary(Binary[OffsetTable[Pos]+1+j]);
    startpos := OffsetTable[pos] + 2 + j;
    ReffsCount := BinaryToInt(buff);  
  end;
  
  //getting refs
  SetLength(ObjReffs, ReffsCount);
  //writeln('Count of objects:',ReffsCount,'StartPos:',startpos);
  
  shift := 0;
  for i := 0 to ReffsCount-1 do
  begin
    buff := '';
    for j := 0 to RefSize-1 do
      buff := buff + ShowAsBinary(Binary[startpos + j + shift]);
    
    ObjReffs[i] := BinaryToInt(buff);
    //writeln(ObjReffs[i]); 
    shift := shift + RefSize;
  end;
 
  //converting objects
  for i := 0 to ReffsCount-1 do
  begin
     case TypeOfObject(ObjReffs[i]) of
      DICT: 
      begin 
        GetArray := GetDict(ObjReffs[i], RefSize);
        Delete(DictSpace,1,4);
      end;
      ARR:
      begin
        GetArray := GetArray(ObjReffs[i], RefSize);
        Delete(DictSpace,1,4);
      end;
      STR:  GetArray := GetStr(ObjReffs[i], false);
      BOOL: GetArray := GetBool(ObjReffs[i]);
      INT:  GetArray := GetInt(ObjReffs[i], false);
      RL:   GetArray := GetReal(ObjReffs[i],false);
      DATE: GetArray := GetDate(ObjReffs[i],false);
      DATA: GetArray := GetData(ObjReffs[i],false);
      UTF:  GetArray := GetUnicode(ObjReffs[i],false);
      UID:  GetArray := GetUid(ObjReffs[i], false);
      FILL: GetArray := true;
      EMP:  GetArray := true;
    end;
  
  end;
  ObjReffs := nil;
  
  writeln(out,DictSpace+'    </array>');
end;

function GetDict(Pos: int64; RefSize: integer): boolean;
var
 temp, buff: string;
 ReffsCount, i, j, startpos, shift:longint;
 KeysReffs: array of int64;
 ObjReffs: array of int64;
begin
  DictSpace := DictSpace + '    ';
  ReffsCount := 0;
  i := 0; j := 0;
  startpos := 0;
  temp := '';
  buff := '';
  temp := ShowAsBinary(Binary[OffsetTable[Pos]]);
  delete(temp,1,4);

  //getting number of refs in dict
  if temp <> '1111' then
   begin
     ReffsCount := BinaryToInt(temp);
     startpos := OffsetTable[Pos] + 1;
   end
  else
  begin
    temp := ShowAsBinary(Binary[OffsetTable[Pos]+1]);
    delete(temp,1,4);
    i := pow(2,BinaryToInt(temp));
    buff := '';
    for j := 1 to i do
    buff := buff + ShowAsBinary(Binary[OffsetTable[Pos]+1+j]);
    startpos := OffsetTable[pos] + 2 + j;
    ReffsCount := BinaryToInt(buff);  
  end;
  
  //getting refs
  SetLength(KeysReffs, ReffsCount);
  SetLength(ObjReffs, ReffsCount);

  //refs to keys
  shift := 0;
  for i := 0 to ReffsCount-1 do
  begin
    buff := '';
    for j := 0 to RefSize-1 do
      buff := buff + ShowAsBinary(Binary[startpos + shift + j]);
   
    KeysReffs[i] := BinaryToInt(buff); 
    shift := shift + RefSize;
  end;
  
  //refs to values
  shift := 0;
  startpos := startpos + ReffsCount;
  for i := 0 to ReffsCount-1 do
  begin
    buff := '';
    for j := 0 to RefSize-1 do
      buff := buff + ShowAsBinary(Binary[startpos + j + shift]);
    
    ObjReffs[i] := BinaryToInt(buff);
    //writeln(ObjReffs[i]); 
    shift := shift + RefSize;
  end;
  
  writeln(out,DictSpace+'<dict>');
  //converting objects
  for i := 0 to ReffsCount-1 do
  begin
    case TypeOfObject(KeysReffs[i]) of
      DICT: 
      begin
        GetDict := GetDict(KeysReffs[i], RefSize);
        Delete(DictSpace,1,4);
      end;
      ARR:
      begin 
        GetDict := GetArray(KeysReffs[i], RefSize);
        Delete(DictSpace,1,4);
      end;
      STR:  GetDict := GetStr(KeysReffs[i], true);
      BOOL: GetDict := GetBool(KeysReffs[i]);
      INT:  GetDict := GetInt(KeysReffs[i], true);
      RL:   GetDict := GetReal(KeysReffs[i], true);
      DATE: GetDict := GetDate(KeysReffs[i], true);
      DATA: GetDict := GetData(KeysReffs[i], true);
      UTF:  GetDict := GetUnicode(KeysReffs[i], true);
      UID:  GetDict := GetUid(KeysReffs[i], true);
      FILL: GetDict := true;
      EMP:  GetDict := true;
    end;

     case TypeOfObject(ObjReffs[i]) of
      DICT: 
      begin 
        GetDict := GetDict(ObjReffs[i], RefSize);
        Delete(DictSpace,1,4);
      end;
      ARR:  
      begin
        GetDict := GetArray(ObjReffs[i], RefSize);
        Delete(DictSpace,1,4);
      end;
      STR:  GetDict := GetStr(ObjReffs[i], false);
      BOOL: GetDict := GetBool(ObjReffs[i]);
      INT:  GetDict := GetInt(ObjReffs[i], false);
      RL:   GetDict := GetReal(ObjReffs[i],false);
      DATE: GetDict := GetDate(ObjReffs[i],false);
      DATA: GetDict := GetData(ObjReffs[i],false);
      UTF:  GetDict := GetUnicode(ObjReffs[i],false);
      UID:  GetDict := GetUid(ObjReffs[i], false);
      FILL: GetDict := true;
      EMP:  GetDict := true;
    end;
  
  end;
  writeln(out,DictSpace+'</dict>');
  ObjReffs := nil;
  KeysReffs := nil;
end;

function GetTopObject(Pos: int64; RefSize: integer):boolean;
begin
  DictSpace := '';
  case TypeOfObject(Pos) of
    DICT: GetTopObject := GetDict(Pos, RefSize);
    ARR:  GetTopObject := GetArray(Pos, RefSize);
    STR:  GetTopObject := GetStr(Pos, false);
    BOOL: GetTopObject := GetBool(Pos);
    INT:  GetTopObject := GetInt(Pos, false);
    RL:   GetTopObject := GetReal(Pos, false);
    DATE: GetTopObject := GetDate(Pos, false);
    DATA: GetTopObject := GetData(Pos, false);
    UTF:  GetTopObject := GetUnicode(Pos, false);
    UID:  GetTopObject := GetUid(Pos, false);
    FILL: GetTopObject := false;
    EMP:  GetTopObject := false;
  end;
end;

//true if the file is a binary plist
function CanConvert(path: string):boolean;
var
  inp: text;
  bf: string[6];
begin
 CanConvert := false;
 if FileExists(path) then
 begin
  Assign(inp, path);
  FileMode := 0;
  reset(inp);
  read(inp, bf);
  close(inp);
  if bf = 'bplist' then
    CanConvert := true;
 end;
end;

procedure AddHeader(var outfile: text);
begin
 writeln(outfile,'<?xml version="1.0" encoding="UTF-8"?>');
 writeln(outfile,'<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">');
 writeln(outfile,'<plist version="1.0">'); 
end;

//convert binary plist file 'input' to XML plist file 'output'
function BinaryToXML(inputp,outputp:string):boolean;
var 
 Inf:file of byte;
 i, j, shift: longint;
 OffsetSize, ObjRefSize: integer;
 NumObjects, TopObject,
 OffsetTableOffset: int64;
 buffstr: string;
begin
 i := 0;
 shift := 0;
 BinaryToXML := false;

 if FileExists(inputp) then
 begin
   if CanConvert(inputp) then
   begin
    BinaryToXML := true;
    SetLength(Binary, 100);
    Assign(out,outputp);
    rewrite(out);
    If DEBUG then
      writeln('Converting binary file ',inputp,' to XML file ',outputp);
    Assign(inf,inputp);
    FileMode := 0;
    reset(inf);
  
    //loading binary file in the array
    while not eof(inf) do
    begin
      if i = (Length(Binary)-1) then
	SetLength(Binary,Length(Binary)+100);
      read(inf, Binary[i]);
      i := i + 1;
    end;
    close(inf);
    SetLength(Binary,i);

    //get trailer
    OffsetSize := Binary[length(Binary) - 26];
    ObjRefSize := Binary[length(Binary) - 25];
  
    buffstr := '';
    for i := 0 to 7 do
      buffstr := buffstr + ShowAsBinary(Binary[(length(Binary)-24)+i]);
    NumObjects := BinaryToInt(Buffstr);
  
    buffstr := '';
    for i := 0 to 7 do
      buffstr := buffstr + ShowAsBinary(Binary[(length(Binary)-16)+i]);
    TopObject := BinaryToInt(buffstr);

    buffstr := '';
    for i := 0 to 7 do
      buffstr := buffstr + ShowAsBinary(Binary[(length(Binary)-8)+i]);
    OffsetTableOffset := BinaryToInt(buffstr);
    
    if DEBUG then
    begin
      writeln('Offsets are ',OffsetSize,' bytes');
      writeln('Object Refs are ',ObjRefSize,' bytes');
      writeln('There are ',NumObjects,' objects in the file');
      writeln('The top object is at ',TopObject);
      writeln('The Offset Table is at offset ',OffsetTableOffset);
    end;
    //getting offset table
    SetLength(OffsetTable, NumObjects);

    for i := 0 to NumObjects -1 do
    begin
      buffstr := '';

      for j:= 0 to OffsetSize-1 do
	buffstr := buffstr + ShowAsBinary(Binary[OffsetTableOffset+shift+j]);

      OffsetTable[i] := BinaryToInt(buffstr);
      //writeln('Object(',i,') at position: ', OffsetTable[i]);
      shift := shift + OffsetSize;
    end;
  
    //adding header to output file
    AddHeader(out);

    //converting top object 
    BinaryToXML := GetTopObject(TopObject, ObjRefSize);

    if BinaryToXML then
      begin
      writeln(out,'</plist>');
      close(out); 
      end
    else
      begin
	close(out);
	erase(out);
      end;

    Binary := nil;
    OffsetTable := nil;
   end
   else
     writeln('File ',inputp,' is not a binary plist!');
 end
 else
  writeln('File ', inputp, ' not found!');
end;

end.
